#include "Number.h"
#include <math.h>

namespace SymCe{
	Number::Number(){
		type = FRACTION;
		fraction = 1;
	}

	Number::Number(const Fraction &fraction_){
		type = FRACTION;
		fraction = fraction_;
	}

	
	Number::Number(int i_){
		type = FRACTION;
		fraction = i_;
	}

	Number::Number(long double d_){
		type = REAL;
		real = d_;
	}

	Number::Number(double d_){
		type = REAL;
		real = d_;
	}

	Number::Number(const Number & num){
		type = num.type;
		fraction = num.fraction;
		real = num.real;
	}


	Number::~Number(){};

	Number Number::operator +() const{
		return *this;
	}

	Number Number::operator-() const{
		if(type == REAL){
			return Number(-real);
		}else{
			return Number(-fraction);
		}
	}

	Number Number::operator+(const Number & num) const{
		if((type == FRACTION) && (num.type == FRACTION)){
			return Number(fraction + num.fraction);
		}else{
			return Number(get_double() + num.get_double());
		}
	}

	Number Number::operator*(const Number & num) const{
		if((type == FRACTION) && (num.type == FRACTION)){
			return Number(fraction * num.fraction);
		}else{
			return Number(get_double() * num.get_double());
		}
	}

	Number Number::operator -(const Number & num) const{
		return (*this + (-num));
	}

	Number Number::operator /(const Number & num) const{
		if((type == FRACTION) && (num.type == FRACTION)){
			return Number(fraction / num.fraction);
		}else{
			return Number(get_double() / num.get_double());
		}
	}

	bool Number::operator <(const Number & num) const{
		if((type == FRACTION) && (num.type == FRACTION)){
			return fraction < num.fraction;
		}else{
			return get_double() < num.get_double();
		}
	}

	bool Number::operator >(const Number & num) const{
		if((type == FRACTION) && (num.type == FRACTION)){
			return fraction > num.fraction;
		}else{
			return get_double() > num.get_double();
		}
	}

	bool Number::operator <=(const Number & num) const{
		if((type == FRACTION) && (num.type == FRACTION)){
			return fraction <= num.fraction;
		}else{
			return get_double() <= num.get_double();
		}
	}

	bool Number::operator >=(const Number & num) const{
		if((type == FRACTION) && (num.type == FRACTION)){
			return fraction >= num.fraction;
		}else{
			return get_double() >= num.get_double();
		}
	}

	bool Number::operator ==(const Number & num) const{
		if((type == FRACTION) && (num.type == FRACTION)){
			return fraction == num.fraction;
		}else{
			return get_double() == num.get_double();
		}
	}

	bool Number::operator !=(const Number & num) const{
		if((type == FRACTION) && (num.type == FRACTION)){
			return fraction != num.fraction;
		}else{
			return get_double() != num.get_double();
		}
	}

	Number & Number::operator =(const Number & num){
		type = num.type;
		fraction = num.fraction;
		real = num.real;
		return * this;
	}

	Number Number::operator^(const Number & num){
		Number tmp = *this;
		Number to_return(1);
		if (num.is_int()){
			if (num < Number(0)){
				tmp = Number(1)/tmp;
			}
			for (Integer i = 0; i < num.fraction.get_numerator(); i++){
				to_return = to_return * tmp;
			}
			return to_return;
		}else{
			return Number(exp( num.get_double() * log((*this).get_double())));
		}
	}
	


	/*Number::operator long double(){
		return get_double();
	}*/

	long double Number::get_double() const{
		if(type == REAL){
			return real;
		}else{
			long double p = fraction.get_numerator();
			long double q = fraction.get_denominator();
			long double to_return = p/q;
			if(fraction.is_negative()){
				return -to_return;
			}else{
				return to_return;
			}
		}
	}

	bool Number::is_int() const{
		if((type == FRACTION) && (fraction.get_denominator() == 1)){
			return true;
		}else{
			return false;
		}
	}

	bool Number::is_int(){
		if((type == FRACTION) && (fraction.get_denominator() == 1)){
			return true;
		}else{
			return false;
		}
	}
		
	std::ostream & operator<<(std::ostream & s, const Number & number){
		if(number.type == Number::FRACTION){
			s << number.fraction;
		}else{
			s << number.real;
		}
		return s;
	}

}
